#include "Campaign.h"
#include "Attribute.h"
#include "Skill.h"
#include "Feat.h"
#include "Special.h"

#include <QCoreApplication>
#include <QFile>
#include <QDomDocument>


using namespace d20;


namespace
{
QString fs_NAME_BASE = QT_TRANSLATE_NOOP("Campaign", "New Campaign");
}



QList<Campaign*> Campaign::instances;



Campaign::Campaign(QObject* parent) : Item(d20Type_Unknown, parent)
{
    data = new Data;

    connect(this, SIGNAL(sigChanged()), this, SLOT(onChanged()));
    connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(deleteLater()));
}



Campaign::Campaign(Campaign& other) : Item(other), Serializable(other)
{
    data = other.data;
}


Campaign* Campaign::create(const QString& try_name)
{
    QString new_name = try_name;
    if (new_name.isEmpty()) new_name = fs_NAME_BASE;

    int suffix = 1;
    foreach (Campaign* c, instances)
    {
        if (c->name() != new_name) break;
        new_name = QString("%1 (%2)").arg(try_name).arg(++suffix);
    }

    Campaign* c = new Campaign();
    c->setName(new_name);
    c->setDirty(true);

    instances.append(c);
    return c;
}


Campaign* Campaign::instance(const QString& name)
{
    foreach (Campaign* c, instances)
    {
        if (c->name() == name) return c;
    }

    return create(name);
}


void Campaign::destroy(const QString& name)
{
    foreach (Campaign* c, instances)
    {
        if (!c || c->name() != name) continue;

        instances.removeAt(instances.indexOf(c));
        c->deleteLater();
        return;
    }

}



const QList<Campaign*> Campaign::all()
{
    return Campaign::instances;
}



void Campaign::setName(const QString &name)
{
    if (name == Item::name()) return;


    Item::setName(name);
    foreach (Skill* item, skills())
    {
        if (!item) continue;

        item->setCampaign(Item::name());
    }
}


Item* Campaign::createItem(int type, const QString& name)
{
    QString try_name = name;
    int i = 0;

    while (data->items.contains(ID(try_name, type)))
    {
        try_name = QString("%1 (%2)").arg(name).arg(++i);
    }

    Item* item = 0;
    switch (type)
    {
    case d20Type_Attribute:
        {
            item = new Attribute(this);
            break;
        }
    case d20Type_Skill:
        {
            item = new Skill(this);
            break;
        }
    case d20Type_Feat:
        {
            item = new Feat(this);
            break;
        }
    case d20Type_Special:
        {
            item = new Special(this);
            break;
        }
    default:
        return 0;
    }

    if (!item) return 0;


    item->setName(try_name);

    if (item->id().isNull())
    {
        qWarning("Item ID is still null!");
    }

    addItem(item);
    return item;

}



void Campaign::removeItem(int id)
{

    if (!data->items.contains(id))
    {
        return;
    }

    Item* item = data->items.take(id);
    if (!item) return;

    item->deleteLater();
    emit sigChanged();
    return;
}



void Campaign::addItem(Item* item)
{
    if (!item || item->id().isNull()) return;

    data->items.insert(item->id().toInt(), item);

    connect(item, SIGNAL(sigChanged()), this, SIGNAL(sigChanged()));
    connect(item,   SIGNAL(sigRegenId(QVariant)), this, SLOT(onRegenId(QVariant)));
    emit sigChanged();
}



Item* Campaign::item(const qint32& id)
{
    if (!data->items.contains(id)) return 0;

    return data->items.value(id);
}



QList<Item*> Campaign::items() const
{
    return data->items.values();
}



Attribute* Campaign::attribute(const qint32& id)
{
    return qobject_cast<Attribute*>(item(id));
}



Skill* Campaign::skill(const qint32& id)
{
    return qobject_cast<Skill*>(item(id));
}



Attribute* Campaign::attribute(const QString& name)
{
    return attribute(ID(name, d20Type_Attribute));
}



Skill* Campaign::skill(const QString& name)
{
    return skill(ID(name, d20Type_Skill));
}



QList<Attribute*> Campaign::attributes() const
{
    QList<Attribute*> ret;
    foreach (Item* item, data->items)
    {
        if (item->type() != d20Type_Attribute) continue;

        Attribute* attribute = qobject_cast<Attribute*>(item);
        if (!attribute) continue;

        ret.append(attribute);
    }

    return ret;
}



QList<Skill*> Campaign::skills() const
{
    QList<Skill*> ret;
    foreach (Item* item, data->items)
    {
        if (item->type() != d20Type_Skill) continue;

        Skill* skill = qobject_cast<Skill*>(item);
        if (!skill) continue;

        ret.append(skill);
    }

    return ret;
}



QDomElement Campaign::toXmlElem(QDomElement& elem) const
{
    QDomDocument document = elem.ownerDocument();
    if (document.isNull()) return elem;

    QDomElement campaign = Item::toXmlElem(elem);


    QDomElement elem_attributes = campaign.firstChildElement(g_ELEM_ATTRIBUTES);
    if (elem_attributes.isNull()) elem_attributes = document.createElement(g_ELEM_ATTRIBUTES);
    campaign.appendChild(elem_attributes);

    foreach (Attribute* a, attributes())
    {
        QDomElement elem = document.createElement(D20_ATTRIBUTE);
        elem = a->toXmlElem(elem);
        elem_attributes.appendChild(elem);
    }


    QDomElement elem_skills = campaign.firstChildElement(g_ELEM_SKILLS);
    if (elem_skills.isNull()) elem_skills = document.createElement(g_ELEM_SKILLS);
    campaign.appendChild(elem_skills);

    foreach (Skill* s, skills())
    {
        QDomElement elem = document.createElement(D20_SKILL);
        elem = s->toXmlElem(elem);
        elem_skills.appendChild(elem);
    }

    return campaign;
}



void Campaign::fromXmlElem(QDomElement &elem)
{
    if (elem.tagName() != "campaign") return;

    blockSignals(true);

    Item::fromXmlElem(elem);

    // Attributes:
    int i = 0;
    QDomElement elem_attribute = elem.firstChildElement(g_ELEM_ATTRIBUTES).firstChildElement(D20_ATTRIBUTE);
    while (!elem_attribute.isNull())
    {
        Attribute* item = qobject_cast<Attribute*>(createItem(d20Type_Attribute, elem_attribute.attribute(g_ATTR_NAME,QString("Unknown attribute %1").arg(++i))));
        if (item)
        {
            item->setValue(elem_attribute.attribute(g_ATTR_VALUE, QString()));
            item->fromXmlElem(elem_attribute);
        }

        elem_attribute = elem_attribute.nextSiblingElement(D20_ATTRIBUTE);
    }

    // Skills:
    i = 0;
    QDomElement elem_skill = elem.firstChildElement(g_ELEM_SKILLS).firstChildElement(D20_SKILL);
    while (!elem_skill.isNull())
    {

        Skill* item = qobject_cast<Skill*>(createItem(d20Type_Skill, elem_attribute.attribute(g_ATTR_NAME,QString("Unknown skill %1").arg(++i))));
        if (item) item->fromXmlElem(elem_skill);

        elem_skill = elem_skill.nextSiblingElement(D20_SKILL);
    }

    blockSignals(false);
}



QString Campaign::serializedData() const
{
    QFile f(":/d20/templates/campaign");
    if (!f.open(QIODevice::ReadOnly)) return QString::null;

    QDomDocument document;
    document.setContent(&f);
    f.close();

    toXmlElem(document.documentElement());
    return document.toByteArray();
}



void Campaign::setSerializedData(const QString& data)
{
    QDomDocument document;
    document.setContent(data);
    fromXmlElem(document.documentElement());
}



void Campaign::onChanged()
{
    setDirty(true);
}



void Campaign::onRegenId(const QVariant &old_id)
{
    if (old_id.isNull()) return;

    Item* item = data->items.take(old_id.toUInt());
    if (!item) return;

    data->items.insert(item->id().toUInt(), item);
}


QVariant Campaign::bonus()
{
    return QVariant();
}





